#!/usr/bin/env python
#
#       Parser.py
#       
#       Copyright 2009 Ben Davis <its.hip@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

import random

import Logic
import Maps
import Options
import Ships
import TestMap as Map
import ToClient

from Clients import Clients
from Maps import Map
from Players import Players

clients = Clients()
map = Map()
players = Players()

global sorrytext
sorrytext = "Sorry, you're not an admin on this server."




class Parser(object):
    def __init__(self, client):
        self.client = client
        self.clients = clients
        self.players = players
        self.commands = [ChangeMap("changemap"), Players("players"), KickName("kickname"),
        KickIP("kickip"), KickPlayer("kickplayer"), Smite("smite"), Crashplz("crashplz"),
        ChoosePlayer("choose"), Winplz("winplz"), Restart("restart"), GetClients("getclients"),
        NameMe("name")]

    def do(self, data):
        """Acts on data inputted"""
        commands = []
        # For each command
        for command in self.commands:
            # If the data starts with a command name
            if data.startswith(command):
                # Then add command to the list
                commands.append(command)
        # If at least one matching command was found
        if len(commands) > 0:
            commands.sort()
            commands.reverse()
            # Automatically take the longest matching command
            commands[0].run(self.client, self.clients, self.players, data[len(commands[0]):].strip())
        # If there was no matching command
        else:
            # Send an error message
            self.client.send(ToClient.Message("Couldn't find command."))

class Command(str):
    """Command base class"""
    def run(self, client, clients, players, data):
        pass;
        
class ChangeMap(Command):
    """Changes to a new map. At the moment, restarts map but in future
    will change to a map specified by name.
    
    Format: change map"""
    
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "changemap":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            clients.send(ToClient.Message("Changing map."))
            #for word in "Sending you lots of messages at once!".split(" "):
                #clients.send(ToClient.Message(word))
            # Loop through each player
            for player in players.values():
                # Delete their current ships
                player.ships = Ships.Ships()
                # Until they have 1 ship
                while len(player.ships.values()) < 1:
                    # Generate a random coordinate on the map
                    x, y = random.randint(0, Map.WIDTH - 1), random.randint(0, Map.HEIGHT - 1)
                    # Check it doesn't collide with any other ships
                    for other in players.values():
                        for ship in other.ships.values():
                            if ship.x == x and ship.y == y:
                                break
                    else:
                        player.ships.append(Ships.Pawn(player, x, y, random.randint(0, 7)))
            # Sync all clients
            for client in clients:
                client.send(ToClient.Sync(client.player, players, Maps.Map(Map)))
            
class Players(Command):
    """Sends a list of the currently connected players to the one asking."""
    
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "players":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        print "admin:", admin
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            client.send(ToClient.Message(">> "+str(len(players))+" Players, "+str(len(clients))+" Clients <<"))
            for player in players.values():
                if player.type == "player":
                    if player.key() == client.player.key():
                        me = "YOU: "
                    else:
                        me = ""
                    name = player.name
                    unid = player.key()
                    playernum = player.number+1
                    ships = len(player.ships.values())
                    client.send(ToClient.Message(">"+ me + name +" ("+ unid +") "+ str(playernum) +", "+ str(ships) +" ships."))


class KickPlayer(Command):
    """Kicks the specified player off of the server."""
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "kickplayer":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            args = data.split(" ")
            reason = ""
            
            if len(args) > 1:
                for arg in args:
                    if arg != args[0]:
                        reason += arg + " "
            else:
                reason = "Kicked by "+ client.player.name
            for guy in clients:
                try:
                    if guy.player.number == int(args[0]) -1:
                        guy.disconnected = True
                        guy.send(ToClient.Kick(reason))
                        client.send(ToClient.Message("Kicked player "+ (args[0]) + "("+reason+")"))
                except ValueError:
                    client.send(ToClient.Message("&kickplayer [1/2/3/4] [reason]"))


class KickName(Command):
    """Kicks the specified player off of the server."""
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "kickname":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            args = data.split(" ")
            reason = ""
            
            if len(args) > 1:
                for arg in args:
                    if arg != args[0]:
                        reason += arg + " "
            else:
                reason = "Kicked by "+ client.player.name
            for guy in clients:
                if guy.player.name == args[0]:
                    guy.disconnected = True
                    guy.send(ToClient.Kick(reason))
                    client.send(ToClient.Message("Kicked player "+ args[0] + "("+reason+")"))
                    
class KickIP(Command):
    """Kicks the specified player off of the server."""
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "kickip":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
    
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            args = data.split(" ")
            reason = ""
            
            if len(args) > 1:
                for arg in args:
                    if arg != args[0]:
                        reason += arg + " "
            else:
                reason = "Kicked by "+ client.player.name
            for guy in clients:
                if guy.player.address == args[0]:
                    guy.disconnected = True
                    guy.send(ToClient.Kick(reason))
                    client.send(ToClient.Message("Kicked player at "+ args[0] + "("+reason+")"))

class Smite(Command):
    """Allows a player to smite ships."""
    
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "smite":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            # probably need admin limits on these.
            args = data.split(" ")
            if args[0] != "p1" and args[0] != "p2":
                client.send(ToClient.Message("&smite [p1/p2] [all/first/rand]"))
            elif len(args) > 1:
                guylol = ""
                if args[1] == "all":
                    for guy in players.values():
                        for ship in guy.ships.values():
                            if args[0] == "p1":
                                #kill blue ships
                                if guy.number == 0:
                                    clients.send(ToClient.Smite(guy.key(), ship.key()))
                                    guylol = guy
                            elif args[0] == "p2":
                                #kill p2's ships
                                if guy.number == 1:
                                    clients.send(ToClient.Smite(guy.key(), ship.key()))
                                    guylol = guy
                    clients.send(ToClient.Message(client.player.name +" smote "+ guylol.name + "'s ships!"))
                    
                elif args[1] == "rand" or args[1] == "first":
                    
                    for guy in players.values():
                        if args[1] == "rand":
                            num = random.randint(0, (len(guy.ships)-1))
                        elif args[1] == "first":
                            num = 0
                        if (guy.number == 0 and args[0] == "p1") or (guy.number == 1 and args[0] == "p2"):
                            print guy.ships.values()
                            ship = guy.ships.values()[num]
                            clients.send(ToClient.Smite(guy.key(), ship.key()))
                            clients.send(ToClient.Message(client.player.name +" smote "+ guy.name + "'s ship!"))
                else:
                    client.send(ToClient.Message("&smite [p1/p2] [all/first/rand]"))
            
            
            
            
class Crashplz(Command):
    """Crashes the server."""
    
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "crashplz":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        print "admin:", admin
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            client.send(ToClient.Message("Crashing and burning, tell my wife hello."))
            raise NameError("Crashed by user plz")


class ChoosePlayer(Command):
    """A text-interface way to choose which player to be, as backup."""
    def run(self, client, clients, players, data):
        try:
            if int(data) >= 0 and int(data) <=2:
                print client, "chose to be player", data
                client.chosen = int(data)
            else:
                client.send(ToClient.Message("&choose: player number invalid."))
        except ValueError:
            client.send(ToClient.Message("&choose [0/1/2]"))
            


            
class Winplz(Command):
    """The player who uses this command is the winner. No questions asked."""
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "winplz":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        print "admin:", admin
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            if map.logic.gameon():
                map.logic.awin(ToClient, [client.player.key()])
            else:
                client.send(ToClient.Message("&winplz: not currently in-game."))


            
class Restart(Command):
    """Restarts the current game."""
    def run(self, client, clients, players, data):
        admin = False
        adminsonly = True
        for command in Options.OPENCMDS:
            if command == "restart":
                adminsonly = False
            
        for name in Options.ADMINS:
            if client.player.name == name:
                admin = True
        print "admin:", admin
        if not admin and adminsonly:
            client.send(ToClient.Message(sorrytext))
        else:
            if map.logic.gameon():
                map.logic.restarting(True)
            else:
                client.send(ToClient.Message("&restart: not currently in-game."))

class GetClients(Command):
    """Refreshes current connected clients."""
    def run(self, client, clients, players, data):
        clients.send(ToClient.ClientListUpdate())

class NameMe(Command):
    """Restarts the current game."""
    def run(self, client, clients, players, data):
        client.player.name = data
        clients.send(ToClient.ClientListUpdate())
        

"""            
class Disconnect(Command):
    def run(self, client, data):
        Connect('localhost')
    
class Connect(Command):
    def run(self, client, clients, players, data):
        if data != "":
            if data.count(" ") == 1:
                address = data.split(" ")[0]
                port = int(data.split(" ")[1])
            else:
                address = str(data)
                port = 7059
                client.send(ToClient.Message("Connect: No port given, trying default (7059)."))
                
            if address.count(".") < 1 and address != "localhost":
                client.send(ToClient.Message("Connect: ["+address+"] is not a valid IP or hostname."))
            else:
                client.send(ToClient.Connect(address, port))
        else:
            client.send(ToClient.Message("Connect: No server given."))

            
class Login(Command):
    def run(self, client, clients, players, data):
        if data != "":
            if data.count(" ") == 1:
                user = data.split(" ")[0]
                password = data.split(" ")[1]
                client.send(ToClient.Login(user, password))
            else:
                client.send(ToClient.Message("Login: format is &login username password."))
        else:
            client.send(ToClient.Message("Login: format is &login username password."))
    
class Logout(Command):
    def run(self, client, clients, players, data):
        client.send(ToClient.Logout())
        client.send(ToClient.Message("Logging off."))
        
class Register(Command):
    def run(self, client, clients, players, data):
        client.send(ToClient.Register())
class Unregister(Command):
    def run(self, client, clients, players, data):
        client.send(ToClient.Unregister())

"""